﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using GraphCompiler.Core.Parsing;
using GraphCompiler.Core.SyntaxRepresentation;
using Rhino.Mocks;
using GraphCompiler.Core;
using Rhino.Mocks.Interfaces;
using System.Threading;
using StructureMap;

namespace GraphCompiler.Specification.Parsing
{
    [TestFixture]
    public class DiagramParserSpecs : SpecificationBase
    {
        DiagramParser _parser;
        DiagramParser _parserWithMockBoxParser;
        DiagramParser _parserWithMockArrowParser;
        ITypedParser<BoxNode> _mockBoxParser;
        ITypedParser<ArrowNode> _mockArrowParser;

        [SetUp]
        public void SetUp()
        {
            _parser = ObjectFactory.GetInstance<DiagramParser>();

            _mockBoxParser = MockRepository.GenerateMock<ITypedParser<BoxNode>>();
            _mockArrowParser = MockRepository.GenerateMock<ITypedParser<ArrowNode>>();

            _parserWithMockBoxParser = new DiagramParser(_mockBoxParser, new ArrowParser());
            _parserWithMockArrowParser = new DiagramParser(new BoxParser(), _mockArrowParser);
        }

        [Test]
        public void GivenDiagramContainingSingleBox_UsesBoxParserOnceWithBoxTokens()
        {
            var tokens = new TokenCollection(
                "diagram",
                "{",
                    "box", "myBox",
                    "{",
                    "}",
                "}");

            var tokensWithoutLeadingDiagramTokens = new TokenCollection(
                    "box", "myBox",
                    "{",
                    "}",
                "}");

            _mockBoxParser.Expect(p => p.IsApplicable(null)).IgnoreArguments().Return(true);
            _mockBoxParser.Expect(p => p.Parse(tokensWithoutLeadingDiagramTokens))
                .Do(new BoxParseDelegate(PerformFakeBoxParsing));

            _mockBoxParser.Replay();

            _parserWithMockBoxParser.ParseTypedNode(tokens);

            _mockBoxParser.VerifyAllExpectations();
        }

        [Test]
        public void GivenDiagramContainingTwoBoxes_UsesBoxParserTwiceWithTokensConsumed()
        {
            var tokens = new TokenCollection(
                "diagram",
                "{",
                    "box", "myBox",
                    "{",
                    "}",
                    "box", "otherBox",
                    "{",
                    "}",
                "}");

            _mockBoxParser.Expect(p => p.IsApplicable(null)).IgnoreArguments().Return(true)
                .Repeat.Twice();
            _mockBoxParser.Expect(p => p.Parse(null))
                .IgnoreArguments()
                .Repeat.Twice()
                .Do(new BoxParseDelegate(PerformFakeBoxParsing));

            _mockBoxParser.Replay();

            _parserWithMockBoxParser.ParseTypedNode(tokens);

            _mockBoxParser.VerifyAllExpectations();
        }

        [Test]
        public void GivenDiagramContainingArrowDeclaration_UsesArrowParserOnce()
        {
            var tokens = new TokenCollection(
                "diagram",
                "{",
                    "box", "myBox", "{", "}",
                    "box", "otherBox", "{", "}",
                    "myBox", "->", "otherBox",
                "}");

            var tokensWithBoxesConsumed = new TokenCollection(
                "myBox", "->", "otherBox",
                "}");

            _mockArrowParser.Expect(p => p.IsApplicable(null)).IgnoreArguments().Return(true);
            _mockArrowParser.Expect(p => p.Parse(tokensWithBoxesConsumed))
                .Do(new ArrowParseDelegate(PerformFakeArrowParsing));

            _mockArrowParser.Replay();

            _parserWithMockArrowParser.ParseTypedNode(tokens);

            _mockArrowParser.VerifyAllExpectations();
        }

        [Test]
        public void GivenValidDiagramDeclarationWithInvalidContent_DoesNotSpinLock()
        {
            int parseTimeoutInMilliseconds = 3000;

            var tokens = new TokenCollection(
                "diagram", 
                "{", 
                    "invalidtoken", "invalidtoken", "invalidtoken", 
                "}");

            Thread t = new Thread(() => _parser.Parse(tokens));
            t.Start();

            bool wasFinishedBeforeTimeout = t.Join(parseTimeoutInMilliseconds);

            Assert.IsTrue(wasFinishedBeforeTimeout);
        }

        [Test]
        public void GivenDiagramWithTwoBoxesAndOneArrow_ReturnsNodeWithCorrespondingChildNodes()
        {
            var tokens = new TokenCollection(
                "diagram",
                "{",
                    "box", "fromBox",
                    "{",
                    "}",
                    "box", "toBox",
                    "{",
                    "}",
                    "fromBox", "->", "toBox",
                "}");

            var expectedNode = new DiagramNode();
            expectedNode.AddChild(new BoxNode("fromBox"));
            expectedNode.AddChild(new BoxNode("toBox"));
            expectedNode.AddChild(new ArrowNode("fromBox", "toBox"));

            var actualNode = _parser.Parse(tokens);

            Assert.AreEqual(expectedNode, actualNode);
        }

        private delegate BoxNode BoxParseDelegate(TokenCollection tokens);
        private delegate ArrowNode ArrowParseDelegate(TokenCollection tokens);

        private BoxNode PerformFakeBoxParsing(TokenCollection tokens)
        {
            for (int i = 0; i < 4; i++)
                tokens.ConsumeNext();

            return new BoxNode("");
        }

        private ArrowNode PerformFakeArrowParsing(TokenCollection tokens)
        {
            for (int i = 0; i < 3; i++)
                tokens.ConsumeNext();

            return new ArrowNode("", "");
        }
    }
}
